import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/app_navigator_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/home_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/signing_up_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/splash_screen_model.dart';
import 'package:website.core/core/application/blocs/auth_bloc/bloc.dart';

part 'bloc_state.dart';

class AppNavigatorBloc extends Cubit<AppNavigatorBlocState> {
  AppNavigatorBloc({
    required AuthBloc authBloc,
  })  : _authBloc = authBloc,
        super(_getInitialState()) {
    _authBlocSubscription = authBloc.stream.listen(_onAuthBlocStateChanged);
  }

  final AuthBloc _authBloc;

  late final StreamSubscription<AuthBlocState> _authBlocSubscription;

  static AppNavigatorBlocState _getInitialState() {
    final pageModels = BuiltList<AppNavigatorPageModel>([
      SplashScreenModel(),
    ]);

    return AppNavigatorBlocState(
      pageModels: pageModels,
    );
  }

  void pushPage({
    required AppNavigatorPageModel pageModel,
  }) {
    final pageModels = state.pageModels.rebuild((pageModels) {
      pageModels.add(pageModel);
    });

    _updatePageStack(
      pageModels: pageModels,
    );
  }

  void pushPageReplacement({
    required AppNavigatorPageModel pageModel,
  }) {
    final pageModels = state.pageModels.rebuild((pageModels) {
      pageModels[pageModels.length - 1] = pageModel;
    });

    _updatePageStack(
      pageModels: pageModels,
    );
  }

  void closePage({
    required AppNavigatorPageModel pageModel,
  }) {
    final pageModels = state.pageModels.rebuild((pageModels) {
      pageModels.remove(pageModel);
    });

    _updatePageStack(
      pageModels: pageModels,
    );
  }

  void replacePageStack({
    required BuiltList<AppNavigatorPageModel> pageModels,
  }) {
    _updatePageStack(
      pageModels: pageModels,
    );
  }

  void handlePop() {
    if (state.pageModels.length > 1) {
      final pageModels = state.pageModels.rebuild((pageModels) {
        pageModels.removeLast();
      });

      _updatePageStack(
        pageModels: pageModels,
      );
    }
  }

  void _updatePageStack({
    required BuiltList<AppNavigatorPageModel> pageModels,
  }) {
    final updatedPageModels = _applyPageGuard(
      pageModels: pageModels,
    );

    emit(state.copyWith(
      pageModels: () => updatedPageModels,
    ));
  }

  BuiltList<AppNavigatorPageModel> _applyPageGuard({
    required BuiltList<AppNavigatorPageModel> pageModels,
  }) {
    final authBlocState = _authBloc.state as InitializedAuthBlocState;
    final userAuthorized = authBlocState is AuthorizedAuthBlocState;

    final updatedPageModels = <AppNavigatorPageModel>[];

    if (userAuthorized) {
      var i = 0;

      while (i < pageModels.length && pageModels[i].permittedWithAuthorization) {
        updatedPageModels.add(pageModels[i]);
        i++;
      }

      if (updatedPageModels.isEmpty) {
        updatedPageModels.add(HomePageModel());
      }
    } else {
      var i = 0;

      while (i < pageModels.length && pageModels[i].permittedWithoutAuthorization) {
        updatedPageModels.add(pageModels[i]);
        i++;
      }

      AppNavigatorPageModel? targetPageModel;

      if (i < pageModels.length) {
        targetPageModel = pageModels[i];
      }

      if (updatedPageModels.length < pageModels.length) {
        updatedPageModels.add(SigningUpPageModel(
          targetPageModel: targetPageModel,
        ));
      }
    }

    return updatedPageModels.build();
  }

  void _onAuthBlocStateChanged(AuthBlocState authBlocState) {
    if (authBlocState is! InitializedAuthBlocState) {
      return;
    }

    if (_authBloc.previousState is AuthorizedAuthBlocState &&
        authBlocState is UnauthorizedAuthBlocState) {
      _updatePageStack(
        pageModels: state.pageModels,
      );
    }
  }

  @override
  Future<void> close() {
    _authBlocSubscription.cancel();
    return super.close();
  }
}
